#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H

#include <QObject>
#include <QWidget>
#include <string>
#include <QString>
#include <QQueue>
#include <QDateTime>
#include <QUrl>
#include <QMimeDatabase>
#include <QUuid>
#include <QTimer>
#include "ffinclude.h"

class DemuxThread;
class DecodeThread;
class AudioDecodeThread;
class TextDecodeThread;
class RenderThread;

//媒体资源信息
struct MediaInfo
{
    MediaInfo(){init();}
    void init();

    //媒体播表信息
    QString id;                //媒体源id
    QString url;               //媒体源地址
    QString programType;       //节目类型：plan(播表)、spot（插播）、shim（垫片）、custom（定制）
    QString programName;       //节目单名称（上传播放日志用）
    QString programId;         //节目单ID（上传播放日志用）
    qint64 startPos;           //指定媒体正放/倒放时的起始播放位置(ms)，反向播放时此值必须大于0
    qint64 endPos;             //指定媒体需要播放多久后停止(ms)，如果endPos超过duration，则会循环播放

    //媒体元数据
    bool isQueryed;             //媒体是否已经查询过元信息
    bool isValid;               //媒体地址是否有效    
    QString mediaType;          //媒体类型（video/audio/live/image）
    QString containerFormat;    //封装格式
    QString videoFormat;        //视频流格式
    QString audioFormat;        //音频流格式
    qint64 duration;            //码流总时长（ms）
    int videoFrameWidth;        //视频帧宽度
    int videoFrameHeight;       //视频帧高度
    int videoFramerate;         //视频流帧率
    int videoBitrate;           //视频流码率
    int audioSamplerate;        //音频流采样率
    int audioChannels;          //音频流声道数
    int audioBitrate;           //音频流码率
    qint64 fileSize;            //文件大小（字节）


    //媒体状态数据
    int errorCount;     //媒体连续播放失败次数
    bool isSeeked;      //媒体是否已经定位过
    qint64 reminTime;   //媒体暂停后剩余播放时长
};

//播放器配置
struct PlayerConfig{
    //拉流
    double speed;           //播放倍速
    qint64 startPos;        //起始播放位置（ms）
    int timeout;            //拉流超时时间（ms）
    bool rtspWithTcp;       //RTSP拉流时是否使用TCP传输
    bool videoEnable;       //是否播放视频流
    bool audioEnable;       //是否播放音频流
    bool textEnable;        //是否播放字幕流
    bool loop;              //是否循环播放文件
    qint64 maxProbeSize;    //码流分析最大读取数据量（kb）

    //解码
    QString vcodecName;     //视频解码器
    QString acodecName;     //音频解码器
    int lowers;             //降分辨等级（值越大降分辨率程度越高，但会损失图像质量）
    bool fastMode;          //是否启用快速解码（启用能够加快解码，但会损失图像质量）
    int codecThreads;       //解码器线程数（默认自动选择）
    bool reorderPts;        //是否需要让解码器重新排布pts
    bool framedrop;         //是否丢包


    //渲染
    const void* winId;      //视频渲染窗口
    int volume;             //输出音量百分比
    int muted;              //是否静音    
    bool fill;              //是否按填充模式渲染视频
    bool keepLastFrame;     //播放结束时是否保持显示最后一帧
};

/*
    视频播放器
*/
class VideoPlayer : public QObject
{
    Q_OBJECT
public:
    static QString createMediaPreview(const QString& mediaFilePath,const QSize& size=QSize());
    static void queryMediaInfo(const QUrl& url,MediaInfo& mediaInfo);
    static QString dumpMediaInfo(const MediaInfo& media);

public:
    explicit VideoPlayer(QObject* parent=nullptr);
    ~VideoPlayer();

    //播放控制
    void play(MediaInfo media);
    void stop();
    void pause();
    void resume();
    bool step(qint64 num=1000);
    bool seek(qint64 pos);

    //播放设置
    PlayerConfig* config();
    MediaInfo mediaInfo();

    //播放器信息
    qint64 position();
    MediaInfo* media();


    WId wId();
    void setWId(WId id);
    bool fill();
    void setFill(bool fill);
    double rate();
    void setRate(double rate);
    bool mute();
    void setMute(bool mute);
    int volume();
    void setVolume(int volume);
    bool enableHWA();
    void setEnableHWA(bool enable);
    bool showFirstFrame();
    void setShowFirstFrame(bool show);
    int posNotifyInterval();
    void setPosNotifyInterval(int intervalMs, bool notifyWithSec=true);
    void setRenderSize(QSize size);
    void setVideSinkName(const QString& vsink);


signals:
    void playerPlaying();                   //播放开始
    void playerPaused();                    //播放暂停
    void playerStopped();                   //播放停止
    void playerError(const QString& errMsg);//播放出现错误
    void playerEnded();                     //播放到达结尾
    void requestRetryPlay();                //指示需要重试播放操作
    void positionChanged(qint64 curPoss);   //播放进度改变

private slots :   
    void initPlayerInfo();
    void destoryPlayerInfo();

    void __error(const QString &errMsg);

    void onDemuxThreadInited();
    void onDemuxThreadError(const QString& errMsg);
    void onDemuxThreadEof();
    void onDemuxThreadSeekDone(bool success,const QString& errMsg);
    void onDemuxThreadExited();

    void onVideoDecodeThreadInited();
    void onVideoDecodeThreadError(const QString& errMsg);
    void onVideoDecodeThreadExited();

    void onAudioDecodeThreadInited();
    void onAudioDecodeThreadError(const QString& errMsg);
    void onAudioDecodeThreadExited();

    void onTextDecodeThreadInited();
    void onTextDecodeThreadError(const QString& errMsg);
    void onTextDecodeThreadExited();

    void onRenderThreadInited();
    void onRenderThreadError(const QString& errMsg);
    void onRenderThreadExited();

private:
    MediaInfo m_mediaInfo;
    PlayerInfo* m_playerInfo;
    PlayerConfig m_playerConfig;

    DemuxThread* m_demuxThread;
    DecodeThread* m_vcodecThread;
    DecodeThread* m_acodecThread;
    DecodeThread* m_textcodecThread;
    RenderThread* m_renderThread;


    QTimer m_endTimer;
    QTimer m_probeCheckTimer;
    QTimer m_posNotifyTimer;

    WId m_wid;

    QMimeDatabase m_mimeDb;
    int m_volume;
    bool m_mute;
    bool m_scaleWithFill;
    double m_rate;
    bool m_enableHWA;
    int m_posNotifyInterval;
    bool m_posNotifyWithSec;
    bool m_showFirstFrame;
    QSize m_renderSize;
    QString m_vsinkName;

    qint64 m_lastNotifyPos;
    bool m_isDestoryed;
    bool m_isReqPlay;
    bool m_isReqSeek;
    bool m_reqPause;

    int m_probeCheckInterval;
    int m_probeCheckMaxTimeout;
    int m_probeCheckMaxErrTimes;
    int m_probeCheckCurErrTimes;
    QDateTime m_probeLastDateTime;

    friend class DemuxThread;
    friend class DecodeThread;
    friend class AudioDecodeThread;
    friend class TextDecodeThread;
    friend class RenderThread;

    friend int read_thread(void *arg);
};

bool init(PlayerInfo* state);
void destory(PlayerInfo* state);

bool open_read_thread(PlayerInfo* state,const char* filename);
void close_read_thread(PlayerInfo *state);
int read_thread(void *arg);
static int stream_component_open(PlayerInfo *is, int stream_index);
static void stream_seek(PlayerInfo *is, int64_t pos, int64_t rel, int seek_by_bytes);
static int stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue);
void stream_close(PlayerInfo *is);

int open_decode_thread(PlayerInfo *state, AVMediaType type);
void close_decode_thread(PlayerInfo *state,AVMediaType type);
int video_decode_thread(void *arg);
int audio_decode_thread(void *arg);
int subtitle_decode_thread(void *arg);

bool open_render_thread(PlayerInfo* state,const void* wid);
void close_render_thread(PlayerInfo *state);
int render_thread(void *arg);
static void video_sub_refresh(void *opaque, double *remaining_time);
double vp_duration(PlayerInfo *is, Frame *vp, Frame *nextvp);
double compute_target_delay(double delay, PlayerInfo *is);
static void update_video_pts(PlayerInfo *is, double pts, int64_t pos, int serial);
static void video_display(PlayerInfo *is);
static int video_open(PlayerInfo *is);
static void video_audio_display(PlayerInfo *s);
static void video_image_display(PlayerInfo *is);
static inline int compute_mod(int a, int b);
int realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture);
void get_sdl_pix_fmt_and_blendmode(int format, Uint32 *sdl_pix_fmt, SDL_BlendMode *sdl_blendmode);
int upload_texture(SDL_Texture **tex, AVFrame *frame, SwsContext **img_convert_ctx) ;
void calculate_display_rect(SDL_Rect *rect, int scr_xleft, int scr_ytop, int scr_width, int scr_height, int pic_width, int pic_height, AVRational pic_sar);
static inline void fill_rectangle(int x, int y, int w, int h);

static int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params);
static void sdl_audio_callback(void *opaque, Uint8 *stream, int len);
static int synchronize_audio(PlayerInfo *is, int nb_samples);
static void update_sample_display(PlayerInfo *is, short *samples, int samples_size);

#endif // VIDEOPLAYER_H
